package de.mmis.core.tuplespace.gui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import de.mmis.core.base.abstracttree.Tree;
import de.mmis.core.base.abstracttree.TreeParserException;
import de.mmis.core.base.device.Device;
import de.mmis.core.sexpression.SExpressionParser;
import de.mmis.core.tuplespace.Tuplespace;
import de.mmis.core.tuplespace.TuplespaceException;

class TupleListEditor extends JFrame {

	private static final long serialVersionUID = 3229338899044682557L;

	static class TupleList {
		public URL url;
		public LinkedList<Tree> expressions = new LinkedList<Tree>();
	}

	private JTextField text_URL;
	private JButton button_loadURL;
	private JTextArea text_list;
	private JScrollPane scroll_list;
	private JButton button_send;
	private JButton button_removeAll;

	private TupleList tupleList;
	final private Device<Tuplespace> space;

	public TupleListEditor(String name, TupleList tupleList,
			Device<Tuplespace> space) {
		super(name);
		this.tupleList = tupleList;
		this.space = space;

		initGUI();
	}

	private void initGUI() {
		GridBagLayout layout = new GridBagLayout();
		layout.rowWeights = new double[] { 0.0, 1.0, 0.0 };
		layout.rowHeights = new int[] { 1, 75, 1 };
		layout.columnWeights = new double[] { 1.0, 0.0 };
		layout.columnWidths = new int[] { 50, 1 };
		setLayout(layout);

		Insets insets = new Insets(5, 5, 5, 5);

		{
			text_URL = new JTextField();
			if (tupleList.url != null)
				text_URL.setText(tupleList.url.toString());
			add(text_URL, new GridBagConstraints(0, 0, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			button_loadURL = new JButton("Load");
			button_loadURL.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent event) {
					load();
				}
			});
			add(button_loadURL, new GridBagConstraints(1, 0, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));
		}

		{
			text_list = new JTextArea();
			text_list.setEditable(true);
			scroll_list = new JScrollPane(text_list);
			add(scroll_list, new GridBagConstraints(0, 1, 2, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.BOTH, insets,
					0, 0));
		}

		{
			button_send = new JButton("send");
			button_send.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					send();
				}
			});
			add(button_send, new GridBagConstraints(0, 2, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));

			button_removeAll = new JButton("remove all");
			button_removeAll.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					removeAllExpressions();
				}
			});
			add(button_removeAll, new GridBagConstraints(1, 2, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					insets, 0, 0));
		}

		pack();
	}

	private void showError(String message, Exception e) {
		JOptionPane.showMessageDialog(this, new Object[] { message, e },
				"Error", JOptionPane.ERROR_MESSAGE);
	}

	private boolean parseExpressions() {
		InputStream is = new ByteArrayInputStream(text_list.getText()
				.getBytes());

		LinkedList<Tree> list = new LinkedList<Tree>();
		try {
			while (is.available() > 0)
				list.add(SExpressionParser.parse(is, false));
		} catch (IOException e) {
			showError("Can't read string", e);
			return false;
		} catch (TreeParserException e) {
			showError("Invalid Format", e);
			return false;
		}

		tupleList.expressions = list;
		return true;
	}

	private void send() {
		if (!parseExpressions())
			return;

		space.ensureAvailability();
		Tuplespace ts = space.getDevice();
		try {
			for (Tree e : tupleList.expressions)
				if (ts.readIfExists(e) == null)
					// only write if not present
					ts.write(e);
		} catch (TuplespaceException e) {
			showError("Can't write to Tuplespace", e);
		} finally {
			space.releaseAvailability();
		}
	}

	private void removeAllExpressions() {
		if (!parseExpressions())
			return;

		space.ensureAvailability();
		Tuplespace ts = space.getDevice();
		try {
			for (Tree e : tupleList.expressions)
				ts.takeIfExists(e);
		} catch (TuplespaceException e) {
			showError("Can't write to Tuplespace", e);
		} finally {
			space.releaseAvailability();
		}
	}

	private void load() {
		URL url;
		try {
			url = new URL(text_URL.getText());
		} catch (MalformedURLException e) {
			showError("Invalid URL", e);
			return;
		}

		InputStream is;
		try {
			is = url.openStream();
		} catch (IOException e) {
			showError("Can't open stream", e);
			return;
		}

		LinkedList<Tree> list = new LinkedList<Tree>();
		try {
			while (is.available() > 0) {
				Tree e = SExpressionParser.parse(is, false);
				list.add(e);
			}
		} catch (IOException e) {
			showError("Error while reading", e);
			return;
		} catch (TreeParserException e) {
			// ignore
		}

		text_list.setText("");
		for (Tree e : list)
			text_list.append(e.toString() + "\n");

		tupleList.expressions = list;
		tupleList.url = url;
	}
}
